library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_signed.all;
entity rolling_average is
	generic (size: integer := 3;
			width:integer:= 8);
	port (
		input_data: in signed (width-1 downto 0);
		output_data: out signed (width-1 downto 0);
		valid: out std_logic;
		clk: in std_logic;
		rst: in std_logic	
	);
end rolling_average;
architecture arch_rolling_average of rolling_average is
component counter is
	generic (max : natural := 2**size - 1);
	port ( enable: in std_logic;
		   current_val: out natural;
		   expired: out std_logic;
		   clk:in std_logic;
		   rst: in std_logic
	);
end component;
component sync_fifo is
	generic (depth: integer := 2**size;
				data_width:integer := width);
	port ( data_in: in std_logic_vector(data_width-1 downto 0);
			 data_out: out std_logic_vector(data_width -1 downto 0);
			 empty: out std_logic;
			 full: out std_logic;
			 internal_cnt: out integer range 0 to depth;
			 wrreq: in std_logic;
			 rdreq: in std_logic;
			 clk: in std_logic;
			 rst: in std_logic
	);	
end component;
type states is (idle,init,after_init);
signal pr_state,nx_state:states;
signal sum : signed(width + size -1 downto 0);
signal fifo_in:std_logic_vector(width-1 downto 0);
signal fifo_out:std_logic_vector(width-1 downto 0);
signal fifo_wrreq: std_logic;
signal fifo_rdreq: std_logic;
signal cnt_en:std_logic;
signal cnt_expired:std_logic;
begin
	U1: sync_fifo port map (
		data_in => fifo_in,
		data_out=> fifo_out,
		empty => open,
		full => open,
		internal_cnt => open,
		wrreq => fifo_wrreq,
		rdreq => fifo_rdreq,
		clk => clk,
		rst => rst
	);
	fifo_wrreq <= '1';
	fifo_in <= conv_std_logic_vector(input_data,width);
	U2: counter port map (
		enable => cnt_en,
		current_val => open,
		expired => cnt_expired,
		clk => clk,
		rst => rst	
	);
	
	
	process(clk,rst)
	begin
		if(rst = '1') then
			pr_state <= idle;
		elsif(clk'event and clk = '1') then
			pr_state <= nx_state;
		end if;
	end process;
	
	process(pr_state,cnt_expired)
	begin
		case pr_state is
			when idle =>
				nx_state <= init;
			when init =>
				if(cnt_expired = '1') then
					nx_state <= after_init;
				else
					nx_state <= pr_state;
				end if;
			when after_init =>
				nx_state <= pr_state;
		end case;
	end process;
	
	process(clk,rst)
	begin
		if(rst = '1') then
			sum <= (others => '0');
			fifo_rdreq <= '0';
			cnt_en <= '1';
			valid <= '0';
		elsif(clk'event and clk = '1') then
			case nx_state is
				when init =>
				  sum <= sum + input_data;
				  fifo_rdreq <= '0';
				when after_init =>
				  sum <= sum - signed(fifo_out)+ input_data;
				  cnt_en <= '0';
				  fifo_rdreq <= '1';
				  valid <= '1';
				when others =>
			end case;
		end if;
	end process;
	output_data <= sum(width + size -1 downto size);
end arch_rolling_average;